例から学ぶ AWS CLI の クエリ(query)活用
はじめに
AWSのマネジメントコンソールから AWS CLIなどコマンドを実行できるサービス、 AWS CloudShell がついにリリースされました。
手元のローカル環境に実行環境構築する必要なく、 ブラウザから AWS CLIや他スクリプトを試行できるのは素晴らしいですね。 AWS CLIの活用がさらに広がること期待しています。
さて、今回は AWS CLIで情報の取捨選択や加工に活用できる クエリ(--query
) の活用例を紹介します。
(もちろん jq
コマンドに慣れている方は、そちら使っていただいて構いません)
※ AWS CLI の クエリの文法は JMESPath に準拠しています。 文法の詳細や JMESPath仕様については、公式ドキュメントを参照ください。
実行環境は以下の通り。もちろん CloudShell上 で実施しました。
aws --version # aws-cli/2.0.58 Python/3.7.3 Linux/4.14.209-160.335.amzn2.x86_64 exec-env/CloudShell exe/x86_64.amzn.2
皆さんも CloudShellを立ち上げてコピペすればすぐに実施できます。 色々と試してみてください。
活用例
- [キー選択] AWSアカウントIDのみ取得
- [リスト参照] CloudFormation(CFn)スタックの一覧を取得
- [入れ子のキー選択] CloudFront ディストリビューションの設定値を取得
- [リスト内 各要素のキー選択] リージョン名やAZ名一覧の取得
- [複数キー選択] IAMユーザー一覧の取得
- [フィルタ] 特定サイズ以上の EBSボリューム一覧の取得
- [パイプ] サブネットの Nameタグ値一覧の取得
- [配列の平坦化] EC2インスタンス一覧の取得
- [関数] 特定文字列を含む名前のCFnスタックを取得、そのスタックのリソース数を取得
[キー選択] AWSアカウントIDのみ取得
sts get-caller-identity
で AWS CLIを実行する 情報(アカウントIDや IAMユーザー or ロール)を取得できます。
以下のような出力です。
aws sts get-caller-identity --output json # { # "UserId": "AROAXXXXXXXXXXXXXXXXX:cm-kawahara.masahiro", # "Account": "123456789012", # "Arn": "arn:aws:sts::123456789012:assumed-role/cm-kawahara.masahiro/xxx-role" # }
AWSアカウントIDのみ取得して、スクリプトで活用すること多いと思います。
--query
を使って Account
キーの値を取得します。
ACCOUNT_ID=`aws sts get-caller-identity --query 'Account' --output text` echo "this AWS Account ID is ${ACCOUNT_ID}" # this AWS Account ID is 123456789012
※テキスト処理などに活用する場合は、出力を text
にすると良いです
[リスト参照] CloudFormation(CFn)スタックの一覧を取得
cloudformation list-stacks
で CloudFormation(CFn)スタックの一覧を
取得できます。
aws cloudformation list-stacks --output yaml # StackSummaries: # - CreationTime: '2020-11-17T03:05:24.552000+00:00' # DriftInformation: # StackDriftStatus: NOT_CHECKED # StackId: arn:aws:cloudformation:ap-northeast-1:123456789012:stack/some-stack/xxxxxxx-xxxx # StackName: hoge-te # StackStatus: CREATE_COMPLETE # - CreationTime: '2020-11-17T03:02:21.905000+00:00' # DeletionTime: '2020-11-17T03:05:07.210000+00:00' # DriftInformation: # StackDriftStatus: NOT_CHECKED # (以下略)
--query 'StackSummaries[0]'
を付けることで、
StackSummaries リストの先頭要素を取得できます。
aws cloudformation list-stacks --query 'StackSummaries[0]' --output yaml # CreationTime: '2020-11-17T03:05:24.552000+00:00' # DriftInformation: # StackDriftStatus: NOT_CHECKED # StackId: arn:aws:cloudformation:ap-northeast-1:123456789012:stack/some-stack/xxxxxxx-xxxx # StackName: hoge-te # StackStatus: CREATE_COMPLETE
以下、ほかのリスト参照の例です。
--query |
出力内容 |
---|---|
StackSummaries[*] |
全て |
StackSummaries[0] |
先頭要素 |
StackSummaries[-1] |
末尾要素 |
StackSummaries[0:5] |
先頭から 5個分の要素 |
StackSummaries[-6:-1] |
末尾から 5個分の要素 |
[入れ子のキー選択] CloudFront ディストリビューションの設定値を取得
cloudfront list-distributions
で CloudFront(CF)ディストリビューションの情報を取得できます。
aws cloudfront list-distributions --output json # { # "DistributionList": { # "Items": [ # { # "Id": "XXXXXXXXXXXXXX", # "ARN": "arn:aws:cloudfront::123456789012:distribution/XXXXXXXXXXXXXX", # "Status": "Deployed", # "LastModifiedTime": "2020-02-21T04:11:45.412000+00:00", # "DomainName": "xxxxxxxxxxxxxx.cloudfront.net", # "Aliases": { # "Quantity": 1, # "Items": [ # "www.example.com" # ] # }, # (以下略)
上記出力のハイライト部分 "Aliases → Items" 内のアドレス
を取得してみます。
以下のような クエリです。
aws cloudfront list-distributions --output text \ --query 'DistributionList.Items[0].Aliases.Items[0]' # www.example.com
.(ドット)
で繋げていくことで、ネストされたデータを取得することができます。
[リスト内 各要素のキー選択] リージョン名やAZ名一覧の取得
ec2 describe-regions
で AWSリージョン一覧を取得できます。
aws ec2 describe-regions --output json # { # "Regions": [ # { # "Endpoint": "ec2.eu-north-1.amazonaws.com", # "RegionName": "eu-north-1", # "OptInStatus": "opt-in-not-required" # }, # { # "Endpoint": "ec2.ap-south-1.amazonaws.com", # "RegionName": "ap-south-1", # "OptInStatus": "opt-in-not-required" # }, # (以下略)
リージョン名のみ一覧をしたいときは以下のような --query
を指定します。
aws ec2 describe-regions --query 'Regions[*].RegionName' --output text # eu-north-1 ap-south-1 eu-west-3 eu-west-2 eu-west-1 ap-northeast-2 ap-northeast-1 sa-east-1 ca-central-1 ap-southeast-1 ap-southeast-2 eu-central-1 us-east-1 us-east-2 us-west-1 us-west-2
text
出力の Tipsですが、
先程の例の
'Regions[*].RegionName'
の部分を 'Regions[*].[RegionName]'
のように角括弧で囲むことで、行単位の出力になります。
aws ec2 describe-regions --query 'Regions[*].[RegionName]' --output text # eu-north-1 # ap-south-1 # eu-west-3 # eu-west-2 # eu-west-1 # (以下略)
行単位の出力にすることで、追加の処理を実施しやすい場合があります。
この出力を使って、 Availability Zone 名 一覧 を出力してみましょう。
aws ec2 describe-regions --query 'Regions[*].[RegionName]' --output text \ | while read region; do aws ec2 describe-availability-zones --region $region \ --query 'AvailabilityZones[*].ZoneName' --output text done # eu-north-1a eu-north-1b eu-north-1c # ap-south-1a ap-south-1b ap-south-1c # eu-west-3a eu-west-3b eu-west-3c # eu-west-2a eu-west-2b eu-west-2c # eu-west-1a eu-west-1b eu-west-1c # ap-northeast-2a ap-northeast-2b ap-northeast-2c ap-northeast-2d # (以下略)
[複数キー選択] IAMユーザー一覧の取得
iam list-users
でIAMユーザー一覧を取得できます。
aws iam list-users --output json #{ # "Users": [ # { # "Path": "/", # "UserName": "AAAA", # "UserId": "AIDAXXXXXXXXXXXXXXXXX", # "Arn": "arn:aws:iam::123456789012:user/AAAA", # "CreateDate": "2019-12-16T06:38:25+00:00" # }, # { # "Path": "/", # "UserName": "BBBB", # "UserId": "AIDAYYYYYYYYYYYYYYYYY", # "Arn": "arn:aws:iam::123456789012:user/BBBB", # "CreateDate": "2020-09-24T02:21:46+00:00", # "PasswordLastUsed": "2020-09-24T02:32:38+00:00" # }, # (以下略)
これをそのまま table
出力すると横幅が長くなり、少々見づらいです。
aws iam list-users --output table # ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # | ListUsers | # +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ # || Users || # |+---------------------------------------------------------------+----------------------------+----------------------------+-------+------------------------+---------------------------------+| # || Arn | CreateDate | PasswordLastUsed | Path | UserId | UserName || # |+---------------------------------------------------------------+----------------------------+----------------------------+-------+------------------------+---------------------------------+| # || arn:aws:iam::123456789012:user/AAAA | 2019-12-16T06:38:25+00:00 | | / | AIDAXXXXXXXXXXXXXXXX | AAAA || # || arn:aws:iam::123456789012:user/BBBB | 2020-09-24T02:21:46+00:00 | 2020-09-24T02:32:38+00:00 | / | AIDAXXXXXXXXXXXXXXXX | BBBB || # (以下略)
そこで ユーザー名、作成日、最後にサインインした日
のみに絞って テーブルに出力してみます。
aws iam list-users --output table \ --query 'Users[*].[UserName,CreateDate,PasswordLastUsed]' # ---------------------------------------------------------------------------- # | ListUsers | # +--------------+-----------------------------+-----------------------------+ # | AAAA | 2019-12-16T06:38:25+00:00 | None | # | BBBB | 2020-09-24T02:21:46+00:00 | 2020-09-24T02:32:38+00:00 | # | CCCC | 2020-01-26T11:30:52+00:00 | None | # | test-user | 2020-06-10T00:47:09+00:00 | 2020-06-10T01:05:21+00:00 | # +--------------+-----------------------------+-----------------------------+
また、 [UserName, CreateDate, PasswordLastUsed]
(→ リスト)の部分を
{Name:UserName, CreateDate:CreateDate, LastUsed:PasswordLastUsed}
(→ ハッシュ)
のように {}
で囲むことで列名を付けることができます。
aws iam list-users --output table \ --query 'Users[*].{Name:UserName, CreateDate:CreateDate, LastUsed:PasswordLastUsed}' # ---------------------------------------------------------------------------- # | ListUsers | # +--------------+-----------------------------+-----------------------------+ # | Name | CreateDate | LastUsed | # +--------------+-----------------------------+-----------------------------+ # | AAAA | 2019-12-16T06:38:25+00:00 | None | # | BBBB | 2020-09-24T02:21:46+00:00 | 2020-09-24T02:32:38+00:00 | # | CCCC | 2020-01-26T11:30:52+00:00 | None | # | test-user | 2020-06-10T00:47:09+00:00 | 2020-06-10T01:05:21+00:00 | # +--------------+-----------------------------+-----------------------------+
※ハッシュにすることで列の順番が保証されなくなります。
そのため、 text
出力で後続の処理で利用する場合は ハッシュ {}
は使わず、 リスト []
で出力しましょう。
[フィルタ] 特定サイズ以上の EBSボリューム一覧の取得
10GiB 以上
のEBSボリュームを取得して概要をテーブル表示します。
aws ec2 describe-volumes --output table \ --query 'Volumes[?Size>=`10`].{ID:VolumeId, Type:VolumeType, Size:Size}' # ------------------------------------------- # | DescribeVolumes | # +------------------------+-------+--------+ # | ID | Size | Type | # +------------------------+-------+--------+ # | vol-04aaaaaaaaaaaaaaa | 50 | gp2 | # | vol-04bbbbbbbbbbbbbbb | 50 | gp2 | # | vol-0eccccccccccccccc | 25 | gp2 | # | vol-06ddddddddddddddd | 10 | gp2 | # | vol-0deeeeeeeeeeeeeee | 10 | gp2 | # | vol-03fffffffffffffff | 10 | gp2 | # +------------------------+-------+--------+
[?(条件式)]
といった書き方でフィルタ出来ます。
リテラル値は バッククォート (``) で囲む必要があります。
以下比較演算子を使えます。
==X
: X と等しい (数値、文字列)!=X
: X と等しくない (数値、文字列)>=X
: X 以上 (数値のみ)<=X
: X 以下 (数値のみ)>X
: X より大きい (数値のみ)<X
: X 未満 (数値のみ)
[パイプ] サブネットの Nameタグ値一覧の取得
サブネットの Nameタグ値一覧 を取得してみます。
aws ec2 describe-subnets --output table \ --query 'Subnets[*].{ ID:SubnetId, Name:Tags[?Key==`Name`] | [0].Value }' # ------------------------------------------------- # | DescribeSubnets | # +---------------------------+-------------------+ # | ID | Name | # +---------------------------+-------------------+ # | subnet-xxxxxxxx | aaa-subnet | # | subnet-yyyyyyyy | bbb-subnet | # | subnet-zzzzzzzz | ccc-subnet | # +---------------------------+-------------------+
パイプ (|
) を使うことで、直前の出力を使って次の処理を行うことが出来ます。
先程の例、 Tags
の値は 「("Key", "Value" キーからなる)ハッシュのリスト」です。
もちろん "Name" タグにフィルタした Tags[?Key==`Name`]
も「ハッシュのリスト」です。
これをパイプして、次の処理で先頭要素( [0]
)の "Value" キーを選択しています。
ちなみに 以下 2つの書き方は、両方とも同じ結果が得られます。
Tags[?Key==`Name`] | [0].Value
Tags[?Key==`Name`].Value | [0]
※ Tags[?Key==`Name`].Value[0]
は意図した出力になりません。
Value
自体は文字列である(=配列ではない) からです。
[配列の平坦化] EC2インスタンス一覧の取得
EC2インスタンス一覧(インスタンスID, Nameタグ, ステータス)をテーブル出力してみます。
aws ec2 describe-instances --output table \ --query 'Reservations[*].Instances[].{ ID: InstanceId, Name: Tags[?Key==`Name`] | [0].Value, State: State.Name }' # --------------------------------------------------- # | DescribeInstances | # +----------------------+---------------+----------+ # | ID | Name | State | # +----------------------+---------------+----------+ # | i-xxxxxxxxxxxxxxxxx | xxx-instance | stopped | # | i-yyyyyyyyyyyyyyyyy | yyy-instance | stopped | # +----------------------+---------------+----------+
ここで「 'Reservations[*].Instances[]
ではなく ~'Reservations[*].Instances[*]
じゃダメなの?」と思われるかもしれません。
[*]
は リストアクセスの章で説明したとおり リスト内全ての要素 を表すものでした。
[]
を使うことで リストを平坦化した上で、全ての要素 を取得することができます。
EC2の各インスタンスの情報は
「 個々の Reservations
内にある Instances
にリストとして格納されている」
ため、この平坦化が役に立ちます。
実際に以下出力例を見ると、
[*]
と []
の違いがイメージできると思います。
### 平坦化無し aws ec2 describe-instances --output json \ --query 'Reservations[*].Instances[*].InstanceId' # [ # [ # "i-xxxx", # "i-yyyy" # ], # [ # "i-zzzz" # ] # ] ### 平坦化有り aws ec2 describe-instances --output json \ --query 'Reservations[*].Instances[].InstanceId' # [ # "i-xxxx", # "i-yyyy", # "i-zzzz" # ]
[関数] 特定文字列を含む名前のCFnスタックを取得、そのスタックのリソース数を取得
JMESPath にはいくつか関数が提供されています。
今回は contains(特定文字列が含まれているかどうか)
と
length(配列の要素数を返す)
を使った例を紹介します。
特定文字列 (今回は "xxx-stg" )がスタック名に含まれている CFnスタックに絞って、 それらスタックのリソース数を返すスクリプトです。
aws cloudformation describe-stacks --output text \ --query 'Stacks[?contains(StackName, `xxx-stg`)].[StackName]' \ | while read stackname; do echo -n "- ${stackname} : " aws cloudformation describe-stack-resources --output text \ --stack-name $stackname \ --query 'length(StackResources)' done # - xxx-stg-s3 : 2 # - xxx-stg-instances : 4 # - xxx-stg-network : 10
これら関数の詳細や、他に使える関数は JMESPathチュートリアル や JMESPathドキュメント を参照ください。
おわりに
AWS CLIのクエリ(query) 例を紹介しました。
もちろん慣れている方は jq でも構いません。AWS CLIのクエリ --query
は
「別途 他パッケージのインストール不要」、「AWS CLIの出力オプション(table や text)が利用可能」
などいくつかメリットがあると思います。
また、AWS CLI には --filters
オプションがあり、簡単に情報を絞ることが可能です。
aws ec2 describe-instances help
など ヘルプコマンドを実行して、
利用できる フィルタオプションを調べると捗るかもしれません。
以上、少しでもどなたかのお役に立てば幸いです。